unit uALSLocateThread;

interface

uses
  SysUtils, Classes, ActiveX, ComObj, Windows, DLXML,
  uDZSysUtils, uDZThread, uDZLinkList, uDZStr, uDZTrace,
  uDefine, uGxtMobileLocateNative;

type
  TALSLocateThread = class(TDzThread)
  private
    fDoingList: TDzDblLinkList;
    fDoneList: TDzDblLinkList;
    fLocator: ILBMPService;
    function ThreadLoop: Boolean;
    function GetDoingItem(const Phone: AnsiString): TAutoLocMobile;
  protected
    procedure Execute; override;
  end;

implementation

uses
  uCxtConst, uAppCmdLineParams, uAppConfig, uAppLog, uGxtCity;

function StatusCodeFromEnum(status: TLbmpSvcState): Integer;
begin
  case status of
    lssUnknown, lssReged, lssNotSupported: Result := LSS_CODE_REGISTERED;
    lssUnreged: Result := LSS_CODE_UNREGISTERED;
    else Result := LSS_CODE_PENDING;
  end;
end;

procedure TALSLocateThread.Execute;
var
  handles: array[0..0] of THandle;
begin
  handles[0] := hSemphore;
  fLocator := TChinaMobileLbmp.Create;
  fDoingList := TDzDblLinkList.Create(TAutoLocMobile, lmStack, False);
  fDoneList := TDzDblLinkList.Create(TAutoLocMobile, lmStack, False);
  try
    while not Stopped do
    try
      case Self.WaitForMultipleObjects(Length(handles), @handles, INFINITE) of
        WAIT_OBJECT_0: Self.ThreadLoop;
        else Continue;
      end;
    except
    end;
  finally
    CheckFreeAndNil(fDoingList);
    CheckFreeAndNil(fDoneList);
    ExitCode := 0;
    FmtWriteLog(tsInformation, 'locate thread(id = %x) exit.'#10, [ThreadId]);
  end;
end;

function TALSLocateThread.GetDoingItem(
  const Phone: AnsiString): TAutoLocMobile;
begin
  Result := TAutoLocMobile(fDoingList.ListHead);
  while (Result <> nil) and (Result.Phone <> Phone) do
    Result := TAutoLocMobile(Result.Next);
end;

function TALSLocateThread.ThreadLoop: Boolean;
var
  i, cnt, ErrCode: Integer;
  targets: array of TLBMPResult;
  mobile: TAutoLocMobile;
  ErrMsg: WideString;
  city: TCity;
begin
  Result := False;
  fDoingList.Clear;
  Pending.Lock;
  try
    cnt := AppConfig.NumberOfTargetPerLocate;
    if cnt > Pending.Count then cnt := Pending.Count;
    SetLength(targets, cnt);
    for i := Low(targets) to High(targets) do
    begin
      mobile := TAutoLocMobile(Pending.Pop);
      fDoingList.Push(mobile);
      targets[i].Phone := mobile.Phone;
    end;
  finally
    Pending.Release;
  end;
  if cnt = 0 then Exit;
  InterlockedExchangeAdd(Locating, cnt);
  if fLocator.GetAreaCodes(targets, ErrCode, ErrMsg) then
  begin
    for i := Low(targets) to High(targets) do
    begin
      mobile := GetDoingItem(targets[i].Phone);
      if targets[i].Success then
      begin
        InterlockedIncrement(Located);
        mobile.AreaCode := targets[i].AreaCode;
        city := AreaList.GetCityByTelCode(mobile.AreaCode);
        if city = nil then
        begin
          mobile.CityCode := 0;
          mobile.AreaName := WideFormat('area%d', [mobile.AreaCode]);
        end
        else with city do begin
          mobile.CityCode := ID;
          if Province.Name = Name then mobile.AreaName := Name
          else mobile.AreaName := Province.Name + Name;
        end;
        mobile.ErrCode := ERROR_SUCCESS;
        mobile.State := LSS_CODE_REGISTERED;
        FmtWriteLog(tsDebug, 'position of %s: %s'#10, [string(mobile.Phone),
          string(mobile.AreaName)]);
      end
      else begin
        InterlockedIncrement(LocateError);
        mobile.AreaCode := 0;
        mobile.ErrCode := targets[i].ErrCode;
        if Length(targets[i].ErrMsg) > 30 then
          mobile.AreaName := WideFormat('error %d', [targets[i].ErrCode])
        else mobile.AreaName := targets[i].ErrMsg;
        mobile.State := StatusCodeFromEnum(targets[i].State);
        FmtWriteLog(tsError, 'locate error %d(%s[%d]): %s'#10,
          [targets[i].ErrCode, string(mobile.Phone),
          mobile.State, string(mobile.AreaName)]);
      end;
      fDoingList.Delete(mobile);
      fDoneList.Push(mobile);
    end;
    Completed.Lock;
    try
      while fDoneList.Count > 0 do Completed.Push(fDoneList.Pop);
    finally
      Completed.Release;
    end;
    InterlockedExchangeAdd(Locating, -cnt);
  end
  else begin
    if cnt > 1 then
    begin
      uAppLog.WriteLog(tsError, WideFormat('locate error %d: (%s)'#10,
        [ErrCode, ErrMsg]));
      Pending.Lock;
      try
        while fDoingList.Count > 0 do Pending.Push(fDoingList.Pop);
      finally
        Pending.Release;
      end;
      ReleaseSemaphore(hSemphore, 1, nil);
      InterlockedExchangeAdd(Locating, -cnt);
    end
    else begin
      mobile := TAutoLocMobile(fDoingList.Pop);
      mobile.AreaCode := 0;
      mobile.ErrCode := ErrCode;
      mobile.AreaName := ErrMsg;
      mobile.State := StatusCodeFromEnum(targets[0].State);
      FmtWriteLog(tsError, 'locate error %d(%s): %s'#10, [targets[0].ErrCode,
        string(mobile.Phone), string(mobile.AreaName)]);
      Completed.LockPush(mobile);
    end;
  end;
end;

end.

